package checkers;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;

import javax.swing.AbstractAction;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;

/**
 * MenuManager creates a menu bar of options and handles all the actions they
 * perform.
 * 
 * @author Ian
 */

@SuppressWarnings({ "serial" })
public class MenuManager {

	GameUI parent;
	SaveAPI saver;

	JMenuItem saveButton;
	JMenuItem loadButton;
	JMenuItem surrenderButton;
	JMenuItem switchPiecesButton;
	JMenuItem helpButton;
	JMenu newGameMenu;

	/**
	 * Creates a MenuManager and sets references to the parent GameUI and
	 * SaveAPI object.
	 * 
	 * @param parent
	 *            The game's user interface
	 * @param saver
	 *            The object used to save statistics and games
	 */
	public MenuManager(GameUI parent, SaveAPI saver) {
		this.parent = parent;
		this.saver = saver;
	}

	/**
	 * Creates the top menu bar.
	 * 
	 * @return The menu bar to be placed at the top of the game window
	 */
	protected JMenuBar createMenu() {

		JMenuBar menuBar = new JMenuBar();

		JMenu optionsMenu = createOptionsMenu();
		JMenuItem bCredits = new JMenuItem(new creditsAction());
		menuBar.add(optionsMenu);
		menuBar.add(bCredits);

		return menuBar;
	}

	/**
	 * Creates the Options menu. It allows the player to start, save, load, and
	 * surrender games, view data, clear data, and exit the game.
	 * 
	 * @return The Options dropdown menu to be placed in the menu bar.
	 */
	private JMenu createOptionsMenu() {

		JMenu optionsMenu = new JMenu("Options");
		JMenuItem bViewData = new JMenuItem(new viewDataAction());
		JMenuItem bClearData = new JMenuItem(new clearDataAction());

		newGameMenu = new JMenu("New");
		JMenuItem onePlayerButton = new JMenuItem(new new1PlayerAction());
		JMenuItem twoPlayerButton = new JMenuItem(new new2PlayerAction());
		saveButton = new JMenuItem(new saveGameAction());
		loadButton = new JMenuItem(new loadGameAction());
		surrenderButton = new JMenuItem(new surrenderGameAction());
		switchPiecesButton = new JMenuItem(new switchPiecesAction());
		helpButton = new JMenuItem(new helpAction());
		JMenuItem exitButton = new JMenuItem(new exitGameAction());

		optionsMenu.add(newGameMenu);
		newGameMenu.add(onePlayerButton);
		newGameMenu.add(twoPlayerButton);
		optionsMenu.add(saveButton);
		optionsMenu.add(loadButton);

		optionsMenu.addSeparator();
		optionsMenu.add(switchPiecesButton);
		optionsMenu.add(helpButton);

		optionsMenu.addSeparator();
		optionsMenu.add(bViewData);
		optionsMenu.add(bClearData);

		optionsMenu.addSeparator();

		optionsMenu.add(surrenderButton);
		optionsMenu.add(exitButton);

		enableButtons(true, false, true, false);

		return optionsMenu;
	}

	/**
	 * Enables or disables various Options menu options to allow the player to
	 * select certain ones only at appropriate times during the game.
	 * 
	 * @param canNew
	 *            Whether the player can start a new game from the menu.
	 * @param canSave
	 *            Whether the player can save the current game.
	 * @param canLoad
	 *            Whether the player can load a saved game.
	 * @param canSurrender
	 *            Whether the player can surrender the current game.
	 */
	public void enableButtons(boolean canNew, boolean canSave, boolean canLoad,
			boolean canSurrender) {
		newGameMenu.setEnabled(canNew);
		saveButton.setEnabled(canSave);
		loadButton.setEnabled(canLoad);
		surrenderButton.setEnabled(canSurrender);
	}

	/* *************************************************************************
	 * Setting up the Actions for the Options Menu.
	 * ************************************************************************
	 */

	/**
	 * An action that begins a 1-player game against the computer.
	 */
	public class new1PlayerAction extends AbstractAction {
		Object[] difficulties = { "Hard", "Medium", "Easy" };

		/**
		 * Places the text "Game vs. Computer..." in any button that uses this
		 * action.
		 */
		public new1PlayerAction() {
			super("Game vs. Computer...");
		}

		/**
		 * Prompts the player for what AI difficulty they would like to play
		 * against, and begins a new game.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {

			int selection = JOptionPane.showOptionDialog(parent,
					"Choose a difficulty:", "New Game vs. AI",
					JOptionPane.YES_NO_CANCEL_OPTION,
					JOptionPane.QUESTION_MESSAGE, null, difficulties,
					difficulties[2]);

			switch (selection) {
			case JOptionPane.CANCEL_OPTION:
				parent.newGame(true, 33, null);
				System.out.println("Easy AI");
				break;
			case JOptionPane.NO_OPTION:
				parent.newGame(true, 66, null);
				System.out.println("Medium AI");
				break;
			case JOptionPane.YES_OPTION:
				parent.newGame(true, 100, null);
				System.out.println("Hard AI");
			default:
				break;
			}
		}
	}

	/**
	 * An action that begins a 2-player game: human vs. human on the same
	 * machine.
	 */
	private class new2PlayerAction extends AbstractAction {

		/**
		 * Places the text "Game vs. Human" in any button that uses this action.
		 */
		public new2PlayerAction() {
			super("Game vs. Human");
		}

		/**
		 * Begins a new game with no AI.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			parent.newGame(false, 0, null);
		}
	}

	/**
	 * An action that serializes the Game instance in order to save a game in
	 * progress.
	 */
	private class saveGameAction extends AbstractAction {
		String message;

		/**
		 * Places the text "Save" in any button that uses this action.
		 */
		public saveGameAction() {
			super("Save");
			message = "Are you sure you want to save?\n"
					+ "This will overwrite any previously saved game.";
		}

		/**
		 * Updates the game playtime and saves the game.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			int selection = JOptionPane.showConfirmDialog(parent, message,
					"Save and Return to Title", JOptionPane.YES_NO_OPTION);
			parent.game.elapsedTime += System.currentTimeMillis()
					- parent.startTime;
			parent.startTime = System.currentTimeMillis();

			switch (selection) {
			case JOptionPane.YES_OPTION:
				if (saver.saveGame(parent.game)) {
					System.out
							.println("This game has been in progress for about "
									+ parent.game.elapsedTime
									/ 1000.0
									+ " seconds.");
					JOptionPane.showMessageDialog(parent,
							"Game saved successfully.", "Save Success",
							JOptionPane.PLAIN_MESSAGE);
					parent.returnToTitle(true);
				} else {
					JOptionPane.showMessageDialog(parent,
							"There was an error saving the game.",
							"Save Failure", JOptionPane.ERROR_MESSAGE);
				}
				break;
			default:
			case JOptionPane.NO_OPTION:
				break;
			}
		}
	}

	/**
	 * An action that inflates the most recently saved Game instance and loads
	 * it for play.
	 */
	private class loadGameAction extends AbstractAction {

		/**
		 * Places the text "Load" in any button that uses this action.
		 */
		public loadGameAction() {
			super("Load");
		}

		/**
		 * Prompts the player about loading a game. First asks if they want to
		 * surrender if they are currently playing a game.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			if (parent.gameInProgress) {
				{
					int selection = JOptionPane.showConfirmDialog(parent,
							"Do you really want to surrender?\n"
									+ "This game will be counted as a loss.",
							"Surrender Game", JOptionPane.YES_NO_OPTION);

					switch (selection) {
					case JOptionPane.YES_OPTION:
						parent.game.elapsedTime += System.currentTimeMillis()
								- parent.startTime;
						saver.addStatistic("loss", parent.game.elapsedTime,
								!parent.ai);
						// Note: parent.ai is true if 2nd player is the
						// computer, but addStatistic wants true for last arg if
						// player2 is human, hence the "!parent.ai".
						parent.returnToTitle(true);
						break;
					default:
					case JOptionPane.NO_OPTION:
						return;
					}
				}
			}
			Game reference = (Game) saver.loadGame();
			if (reference != null) {
				parent.newGame(reference.ai, reference.difficulty, reference);
				System.out.println("Loaded time: " + reference.elapsedTime
						/ 1000.0);
				System.out
						.println("Loaded difficulty: " + reference.difficulty);
				JOptionPane.showMessageDialog(parent, "Saved game loaded.",
						"Load Game", JOptionPane.PLAIN_MESSAGE);
			} else {
				JOptionPane.showMessageDialog(parent,
						"There is no saved game.", "Load Failure",
						JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	/**
	 * An action that ends the game and exits its window.
	 */
	private class exitGameAction extends AbstractAction {
		private String message;

		/**
		 * Places the text "Exit" in any button that uses this action.
		 */
		public exitGameAction() {
			super("Exit");
		}

		/**
		 * Exits the game. Prompts the user to save if a game is in progress, or
		 * exits directly from the title screen.
		 * 
		 * @param e
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent e) {

			if (parent.gameInProgress) {
				message = "Would you like to save this game?";
				int selection = JOptionPane.showConfirmDialog(parent, message,
						"Save and Exit", JOptionPane.YES_NO_OPTION);
				parent.game.elapsedTime += System.currentTimeMillis()
						- parent.startTime;
				parent.startTime = System.currentTimeMillis();

				switch (selection) {
				default:
				case JOptionPane.YES_OPTION:
					if (saver.saveGame(parent.game)) {
						File f = new File("singlelock");
						if (f.exists()) {
							f.delete();
						}
						System.out
								.println("This game has been in progress for about "
										+ parent.game.elapsedTime
										/ 1000.0
										+ " seconds.");
						JOptionPane.showMessageDialog(parent,
								"Game saved successfully.", "Save Success",
								JOptionPane.PLAIN_MESSAGE);
						saver.close(); // safely closes the database connection
						System.exit(0);
					} else {
						JOptionPane.showMessageDialog(parent,
								"There was an error saving the game.",
								"Save Failure", JOptionPane.ERROR_MESSAGE);
					}
					break;
				case JOptionPane.NO_OPTION:
					message = "If you do not save, this game will be counted as"
							+ " a loss.\nDo you want to save?";
					selection = JOptionPane.showConfirmDialog(parent, message,
							"Exit without Saving", JOptionPane.YES_NO_OPTION);
					parent.game.elapsedTime += System.currentTimeMillis()
							- parent.startTime;
					parent.startTime = System.currentTimeMillis();

					switch (selection) {
					default:
					case JOptionPane.YES_OPTION:
						if (saver.saveGame(parent.game)) {
							System.out
									.println("This game has been in progress for about "
											+ parent.game.elapsedTime
											/ 1000.0
											+ " seconds.");
							JOptionPane.showMessageDialog(parent,
									"Game saved successfully.", "Save Success",
									JOptionPane.PLAIN_MESSAGE);
							saver.close(); // safely closes the database
							// connection
							File f = new File("singlelock");
							if (f.exists()) {
								f.delete();
							}
							System.exit(0);
						} else {
							JOptionPane.showMessageDialog(parent,
									"There was an error saving the game.",
									"Save Failure", JOptionPane.ERROR_MESSAGE);
						}
						break;
					case JOptionPane.NO_OPTION:
						File f = new File("singlelock");
						if (f.exists()) {
							f.delete();
						}
						saver.addStatistic("loss", parent.game.elapsedTime,
								!parent.ai);
						saver.close(); // safely closes the database connection
						System.exit(0);
						break;
					}
					break;
				}
			} else {
				message = "Do you really want to exit?";
				int selection = JOptionPane.showConfirmDialog(parent, message,
						"Exit Game", JOptionPane.YES_NO_OPTION);

				switch (selection) {
				case JOptionPane.YES_OPTION:
					File f = new File("singlelock");
					if (f.exists()) {
						f.delete();
					}
					saver.close(); // safely closes the database connection
					System.exit(0);
					break;
				default:
				case JOptionPane.NO_OPTION:
					break;
				}
			}
		}
	}

	/**
	 * An action that surrenders a game in progress.
	 */
	protected class surrenderGameAction extends AbstractAction {
		private String message;

		/**
		 * Places the text "Surrender" in any button that uses this action.
		 */
		public surrenderGameAction() {
			super("Surrender");
			message = "Do you really want to surrender?\n"
					+ "This game will be counted as a loss.";
		}

		/**
		 * Surrenders the game, then prompts the user about starting a new game
		 * or exiting.
		 * 
		 * @param e
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent e) {
			int selection = JOptionPane.showConfirmDialog(parent, message,
					"Surrender Game", JOptionPane.YES_NO_OPTION);

			switch (selection) {
			case JOptionPane.YES_OPTION:
				parent.game.elapsedTime += System.currentTimeMillis()
						- parent.startTime;
				saver.addStatistic("loss", parent.game.elapsedTime, !parent.ai);
				// Note: parent.ai is true if 2nd player is the computer,
				// but addStatistic wants true for last arg if player2 is
				// human, hence the "!parent.ai".

				Object[] options = { "New 1P Game", "New 2P Game", "Exit" };

				selection = JOptionPane
						.showOptionDialog(parent, "What would you like to do?",
								"New Game or Exit",
								JOptionPane.YES_NO_CANCEL_OPTION,
								JOptionPane.QUESTION_MESSAGE, null, options,
								options[2]);

				switch (selection) {
				default:
				case JOptionPane.CANCEL_OPTION:
					saver.close();
					File f = new File("singlelock");
					if (f.exists()) {
						f.delete();
					}
					System.exit(0);
					System.out.println("Exit");
					break;
				case JOptionPane.NO_OPTION:
					parent.newGame(false, 0, null);
					System.out.println("New 2p");
					break;
				case JOptionPane.YES_OPTION:
					new new1PlayerAction().actionPerformed(null);
					System.out.println("New 1P");
					break;
				}
				break;
			default:
			case JOptionPane.NO_OPTION:
				break;
			}
		}
	}

	/**
	 * An action that displays a dialog with a table of saved statistics.
	 */
	private class viewDataAction extends AbstractAction {
		long[] humanStats, aiStats;
		long humanBestTime, aiBestTime;

		/**
		 * Places the text "View Statistics..." in any button that uses this
		 * action.
		 */
		public viewDataAction() {
			super("View Statistics...");
		}

		/**
		 * Shows the statistics from the database.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			humanStats = saver.getStatistics(true);
			aiStats = saver.getStatistics(false);
			humanBestTime = saver.getBestTime(true);
			aiBestTime = saver.getBestTime(false);

			String[] columnNames = { "Statistic", "vs Human", "vs Computer" };
			Object[][] data = {
					{ "Wins", humanStats[0], aiStats[0] },
					{ "Losses", humanStats[1], aiStats[1] },
					{ "Ties", humanStats[2], aiStats[2] },
					{ "Games Played", humanStats[3], aiStats[3] },
					{
							"Best Game Time",
							humanBestTime > 0 ? humanBestTime
									: "No Wins Recorded",
							aiBestTime > 0 ? aiBestTime : "No Wins Recorded" },

			};

			JTable table = new JTable(data, columnNames);
			JScrollPane scrollPane = new JScrollPane(table);
			table.setGridColor(Color.white);
			System.out
					.println(table.getPreferredScrollableViewportSize().height);
			System.out
					.println(table.getPreferredScrollableViewportSize().width);
			scrollPane.setSize(table.getPreferredScrollableViewportSize());
			table.setEnabled(false);
			table.setFillsViewportHeight(true);

			JOptionPane.showMessageDialog(parent, scrollPane, "Statistics",
					JOptionPane.PLAIN_MESSAGE);

		}
	}

	/**
	 * An action that clears the database, including all statistics and saved
	 * games.
	 */
	private class clearDataAction extends AbstractAction {
		private String message;

		/**
		 * Places the text "Clear Data" in any button that uses this action.
		 */
		public clearDataAction() {
			super("Clear Data");
			message = "Do you really want to clear all the game data?";
		}

		/**
		 * Completely clears out the database
		 * 
		 * @param e
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent e) {
			int selection = JOptionPane.showConfirmDialog(parent, message,
					"Confirm Data Deletion", JOptionPane.YES_NO_OPTION);

			switch (selection) {
			case JOptionPane.YES_OPTION:
				saver.reinitialize();
				break;
			default:
			case JOptionPane.NO_OPTION:
				break;
			}
		}
	}

	/**
	 * An action that shows a dialog box with a list of the people who worked on
	 * this game.
	 */
	private class creditsAction extends AbstractAction {
		String message;

		/**
		 * Places the text "Credits" in any button that uses this action.
		 */
		public creditsAction() {
			super("Credits");
			message = "Checkers for CMSC 345\nSpring 2012\n\n"
					+ "Gary Raymond\nIlya Dynin\nZach Hullihen\nTerry McMurry\n"
					+ "David Raabe\nIan McGaughran";
		}

		/**
		 * Shows the game credits.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			JOptionPane.showMessageDialog(parent, message, "Credits",
					JOptionPane.INFORMATION_MESSAGE);
		}
	}

	/**
	 * An action that shows a dialog box with game instructions and information.
	 */
	private class helpAction extends AbstractAction {
		String message;

		/**
		 * Places the text "Help" in any button that uses this action.
		 */
		public helpAction() {
			super("Help");
			message = "Players take turns moving their pieces across the board,"
					+ " trying to capture their opponent's pieces by jumping over "
					+ "them.\nOn your turn, you may slide one of your pieces "
					+ "diagonally toward the opposite side of the board. If on your"
					+ " turn an\nopponent's piece is on a diagonal directly between"
					+ " one of your pieces and an open space, you must jump it. "
					+ "Multiple\njumps in sequence can and must be taken. When any "
					+ "piece reaches the opposite end of the board, it becomes a "
					+ "king.\nA king can move diagonally forward or backward, "
					+ "unlike normal pieces. The winner is the first player to "
					+ "capture all their\nopponent's pieces or force them into a "
					+ "state where they have no possible moves on their turn.\n\n"
					+ "Click on a piece and then on the space you wish to move it "
					+ "to. Click on the selected space again to move there. For\n"
					+ "multiple jumps, click in sequence on each open space in the "
					+ "jump, then click again on the final space. Right click to\n"
					+ "deselect a piece that has been selected.\n\nNote that only "
					+ "one game can be saved at any time.\nYou must surrender a "
					+ "game to the second player in order to exit a game without "
					+ "saving.";
		}

		/**
		 * Shows the game instructions.
		 * 
		 * @param arg0
		 *            The ActionEvent that activated this action.
		 */
		public void actionPerformed(ActionEvent arg0) {
			JOptionPane.showMessageDialog(parent, message, "Checkers Help",
					JOptionPane.INFORMATION_MESSAGE);
		}
	}

	/**
	 * action class for switching the piece set
	 * 
	 * 
	 */
	private class switchPiecesAction extends AbstractAction {

		/**
		 * default contructor to set description
		 */
		public switchPiecesAction() {
			super("Switch Pieces");
		}

		/**
		 * action called when switch piece option is selected
		 * 
		 */
		public void actionPerformed(ActionEvent arg0) {
			parent.switchPieces();
		}
	}

	/**
	 * Listener for closing the game without using the menu.
	 */
	protected class frameListener implements WindowListener {

		/**
		 * Closes the window when the "X" button is clicked.
		 * 
		 * @param arg0
		 *            The event that activated this closing action.
		 */
		public void windowClosing(WindowEvent arg0) {
			new exitGameAction().actionPerformed(null);
		}

		public void windowActivated(WindowEvent arg0) {
		}

		public void windowClosed(WindowEvent arg0) {
		}

		public void windowDeactivated(WindowEvent arg0) {
		}

		public void windowDeiconified(WindowEvent arg0) {
		}

		public void windowIconified(WindowEvent arg0) {
		}

		public void windowOpened(WindowEvent arg0) {
		}
	}
}
